package com.jarveis.frame.security;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Arrays;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import org.apache.log4j.Logger;

import com.jarveis.frame.io.IOUtil;

/**
 * BASE64加密
 * 
 * @author liuguojun
 */
public class BASE64Cipher {

	private static final Logger logger = Logger.getLogger(BASE64Cipher.class);

	private static final char[] CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
			.toCharArray();
	private static final int[] INV = new int[256];

	static {
		Arrays.fill(INV, -1);
		for (int i = 0, iS = CHARS.length; i < iS; i++) {
			INV[CHARS[i]] = i;
		}
		INV['='] = 0;
	}

	/**
	 * 加密
	 * 
	 * @param str
	 * @return String
	 * @throws Exception
	 */
	public static String encrypt(String str) {
		try {
			// byte[] bytes = str.getBytes(CharacterUtil.UTF8);
			byte[] bytes = compress(str);
			return new String(encode(bytes, false));
		} catch (Exception ex) {
			logger.error(ex.getMessage(), ex);
			return null;
		}
	}

	/**
	 * 解密
	 * 
	 * @param str
	 * @return String
	 * @throws Exception
	 */
	public static String decrypt(String str) {
		try {
			byte[] bytes = str.getBytes();
			bytes = decode(bytes);
			return decompress(bytes);
		} catch (Exception ex) {
			logger.error(ex.getMessage(), ex);
			return null;
		}
	}

	/**
	 * Encodes a raw byte array into a BASE64 <code>byte[]</code>.
	 */
	public static byte[] encode(byte[] arr, boolean lineSep) {
		int len = arr != null ? arr.length : 0;
		if (len == 0) {
			return new byte[0];
		}

		int evenlen = (len / 3) * 3;
		int cnt = ((len - 1) / 3 + 1) << 2;
		int destlen = cnt + (lineSep ? (cnt - 1) / 76 << 1 : 0);
		byte[] dest = new byte[destlen];

		for (int s = 0, d = 0, cc = 0; s < evenlen;) {
			int i = (arr[s++] & 0xff) << 16 | (arr[s++] & 0xff) << 8
					| (arr[s++] & 0xff);

			dest[d++] = (byte) CHARS[(i >>> 18) & 0x3f];
			dest[d++] = (byte) CHARS[(i >>> 12) & 0x3f];
			dest[d++] = (byte) CHARS[(i >>> 6) & 0x3f];
			dest[d++] = (byte) CHARS[i & 0x3f];

			if (lineSep && ++cc == 19 && d < destlen - 2) {
				dest[d++] = '\r';
				dest[d++] = '\n';
				cc = 0;
			}
		}

		int left = len - evenlen;
		if (left > 0) {
			int i = ((arr[evenlen] & 0xff) << 10)
					| (left == 2 ? ((arr[len - 1] & 0xff) << 2) : 0);

			dest[destlen - 4] = (byte) CHARS[i >> 12];
			dest[destlen - 3] = (byte) CHARS[(i >>> 6) & 0x3f];
			dest[destlen - 2] = left == 2 ? (byte) CHARS[i & 0x3f] : (byte) '=';
			dest[destlen - 1] = '=';
		}
		return dest;
	}

	/**
	 * Decodes a BASE64 encoded byte array.
	 */
	public static byte[] decode(byte[] arr) {
		int length = arr.length;
		if (length == 0) {
			return new byte[0];
		}

		int sndx = 0, endx = length - 1;
		int pad = arr[endx] == '=' ? (arr[endx - 1] == '=' ? 2 : 1) : 0;
		int cnt = endx - sndx + 1;
		int sepCnt = length > 76 ? (arr[76] == '\r' ? cnt / 78 : 0) << 1 : 0;
		int len = ((cnt - sepCnt) * 6 >> 3) - pad;
		byte[] dest = new byte[len];

		int d = 0;
		for (int cc = 0, eLen = (len / 3) * 3; d < eLen;) {
			int i = INV[arr[sndx++]] << 18 | INV[arr[sndx++]] << 12
					| INV[arr[sndx++]] << 6 | INV[arr[sndx++]];

			dest[d++] = (byte) (i >> 16);
			dest[d++] = (byte) (i >> 8);
			dest[d++] = (byte) i;

			if (sepCnt > 0 && ++cc == 19) {
				sndx += 2;
				cc = 0;
			}
		}

		if (d < len) {
			int i = 0;
			for (int j = 0; sndx <= endx - pad; j++) {
				i |= INV[arr[sndx++]] << (18 - j * 6);
			}
			for (int r = 16; d < len; r -= 8) {
				dest[d++] = (byte) (i >> r);
			}
		}

		return dest;
	}

	/**
	 * 压缩数据
	 * 
	 * @param paramString
	 * @return
	 */
	public static final byte[] compress(String paramString) {
		if (paramString == null)
			return null;
		ByteArrayOutputStream baos = null;
		ZipOutputStream zos = null;
		byte[] arrayOfByte;
		try {
			baos = new ByteArrayOutputStream();
			zos = new ZipOutputStream(baos);
			zos.putNextEntry(new ZipEntry("0"));
			zos.write(paramString.getBytes());
			zos.closeEntry();
			arrayOfByte = baos.toByteArray();
		} catch (IOException ex) {
			arrayOfByte = null;
			logger.error(ex.getMessage(), ex);
		} finally {
			if (zos != null) {
				try {
					zos.close();
				} catch (IOException ex) {
					logger.error(ex.getMessage(), ex);
				}
			}
			if (baos != null) {
				try {
					baos.close();
				} catch (IOException ex) {
					logger.error(ex.getMessage(), ex);
				}
			}
		}
		return arrayOfByte;
	}

	/**
	 * 解压缩数据
	 * 
	 * @param paramBytes
	 * @return
	 */
	public static final String decompress(byte[] paramBytes) {
		if (paramBytes == null)
			return null;
		ByteArrayOutputStream baos = null;
		ByteArrayInputStream bais = null;
		ZipInputStream zis = null;
		String str;
		try {
			baos = new ByteArrayOutputStream();
			bais = new ByteArrayInputStream(paramBytes);
			zis = new ZipInputStream(bais);
			ZipEntry localZipEntry = zis.getNextEntry();
			byte[] arrayOfByte = new byte[1024];
			int i = -1;
			while ((i = zis.read(arrayOfByte)) != -1) {
				baos.write(arrayOfByte, 0, i);
			}
			str = baos.toString();
		} catch (IOException ex) {
			str = null;
			logger.error(ex.getMessage(), ex);
		} finally {
			if (zis != null) {
				try {
					zis.close();
				} catch (IOException ex) {
					logger.error(ex.getMessage(), ex);
				}
			}
			if (bais != null) {
				try {
					bais.close();
				} catch (IOException ex) {
					logger.error(ex.getMessage(), ex);
				}
			}
			if (baos != null) {
				try {
					baos.close();
				} catch (IOException ex) {
					logger.error(ex.getMessage(), ex);
				}
			}
		}
		return str;
	}

	public static void main(String[] args) {
		try {
			String content = "Hello World!";
			System.out.println("content=" + content);
			content = BASE64Cipher.encrypt(content);
			System.out.println("encode=" + content);
			content = BASE64Cipher.decrypt("UEsDBBQACAAIAM6ENUoAAAAAAAAAAAAAAAABAAAAMJVV3U8iVxT/V8wm27etwq6Jm1qT7faxaYzZ96bNvvjQxmTb+so3AwIDfgDCWARBUPkYhcVhZoA/pnPOvTz5L/QcrxLX2k2XByTjvef8vs6Z1febfy5svv/2Gf396ceNZ2sL//FZ3Vr76rdfPmx98/nvBR8amn8JC40FOex7TgP17Cy2i1rOc9pSO5fTLFghMGzQTLAP0EhAvTQrTeikyNbESUuUIljs3bgpOTVEawe1LOwF8Wr0w4afvuW4q65jKiicjmftoJPzxlNvtAOJtMhfyXGPKnh2CQdXs+JUJg9xJ/HwIsRtkTExHBXOObg66Gk5bv8dCIn9S8/OeFYHymOIGZCJYd6E0YBw4lGDiqvrnpVUFbiLFRB9B7QCDhxItgjzkx25ZtoE010kVdBgFYQ9xZ4OuZYcjEQmLqfRWaDiOZltv//rZREaiVYfOw2o/TU7mBKAbd9Lek4gVxe31v6vEWC0MBHw3GPGW4pAtyS1EWQ1yKYIEQu8c0lcRaVPYCHaBt26cbXv1m/cBJmhWkLFJlFWlujz6++iWSG9iTQeVeV1TnF6gZM+1JugnZHmxAzbVW6WqVBRJQLBxn4N8z0cTTzLgGxOCaCoE2mWNNfa9vmWFWHPjm0ve9Y56fDwLqRsqFQIN6T7WBiC3iNAj3rhgTmL6zDRxKApXPs2RYcUJ2UuuDYkr/iKYZIE+PH6LkvJmvxoUlvi5tllcWbL8ZjEktqQAKj6VPzd975XCjqM+nAUJwfZ0P2hKCfF8cDnWXExyUA0zHpE+5wazSRWvlfqMamLvX3Fds7Uv/KcBGegAcYqmmn1r1ktgkbFc0vzoEPG+dIExI+hO8R2DfKG+g3TJrVS00EDdZfE+xH7NLS3MDVWj3OeiGO4hddVnpTECLQLev7IGjkh5iWe30cF6xEWfxJlv5JDDAQZg+XI0yAfvi/yyXSocMzlIvqzwzQHgwdExvuUFjURj3qp2+/e+p6wyBmQljQVrynNL3ycaeULgzAHdIFg/bsjmSns05fLz7lZu0nCoXUGuymOUawNnRNvUsZSfmYE8DJEz99svJnLMbso0rHZqQ5ajIfM3oNOkWNAezDe503p8yvW80i85jjQQoNJgYZFNqaQTopsGK+qvCobBRo0xksGuSUVb29UeejFogKphCAu7IqZo8tzpdiS+xVG4Yby8RfmCjt1qOdvTezwRO+meBfYp1RPhFwmmglB0IDrBg2pDI+h58r4AMsDFQ9Js5k4A2fIAbfrnlOkfQN6kvUeZ96urxM60E9YbyMgpznQc2K/QsbRdgUrjIkMucRbujt8uLywUODXAa3VOu2iJhVZWaKRxOqQem78sf3z5oc5OrW8+W1A7gYvmEH5WP3mxZDv3B1IJ1UlxYDSQLaRmp5Vp0GVuxZZqyDwsb0gq9ktUdjZWgZTaMuTKC+TbhW0Xdb7ljVTS3cxlFCvjM+RImdv/YT8NZr7Ih7G/CHBVSNMJ2UkR4e5smXJZkRJBwexO0uffJMv0rt+7R9QSwcIjshEam8EAAD5BwAA");
			System.out.println("decode=" + content);
		} catch (Exception ex) {

		}
	}
}
